home *** CD-ROM | disk | FTP | other *** search
/ Linux Cubed Series 7: Sunsite / Linux Cubed Series 7 - Sunsite Vol 1.iso / system / news / readers / nn-tk.001 / nn-tk~ / nn / global.c < prev    next >
C/C++ Source or Header  |  1995-07-30  |  21KB  |  1,128 lines

  1. /*
  2.  *    (c) Copyright 1990, Kim Fabricius Storm.  All rights reserved.
  3.  *
  4.  *    Global declarations and auxiliary functions.
  5.  */
  6.  
  7. #include <signal.h>
  8. #include <pwd.h>
  9. #include "config.h"
  10. #include "patchlevel.h"
  11.  
  12. #ifndef HAVE_STDLIB_H
  13. # ifdef VOID_MALLOC
  14. void *malloc();
  15. void *calloc();
  16. void *realloc();
  17. # else
  18. char *malloc();
  19. char *calloc();
  20. char *realloc();
  21. # endif
  22. #endif
  23.  
  24. /* global.c */
  25.  
  26. static sig_type catch_keyboard __APROTO((int n));
  27. static sig_type catch_pipe __APROTO((int n));
  28. static sig_type catch_suspend __APROTO((int n));
  29. static enter_log __APROTO((int type, va_list zZap));
  30. static void mem_error __APROTO((int t, int32 bytes));
  31.  
  32. export char *home_directory;
  33. export char *nn_directory;        /* ~/.nn */
  34. export char *news_directory;        /* /usr/spool/news */
  35. export char *news_lib_directory;     /* /usr/lib/news */
  36. export char *lib_directory;        /* /usr/local/lib/nn */
  37. export char *master_directory;        /* = lib */
  38. export char *help_directory;        /* = lib/help */
  39. export char *bin_directory = BIN_DIRECTORY;
  40.  
  41. export char *db_directory;    /* /usr/spool/nn or NEWS_DIR/.nn */
  42. export char *db_data_directory;    /* ..../DATA     or undefined    */
  43. export int db_data_subdirs = 0;    /* set if DATA/[0-9]/ exist     */
  44.  
  45. export char *news_active;    /* NLIB/active or DB/ACTIVE */
  46.  
  47. export char *pager;
  48.  
  49. export char *log_file;            /* = lib/Log */
  50. export char *log_entry_filter = NULL;
  51.  
  52. export char *temp_file;
  53.  
  54. #ifndef TMP_DIRECTORY
  55. #define TMP_DIRECTORY "/usr/tmp"
  56. #endif
  57. export char *tmp_directory = TMP_DIRECTORY;
  58.  
  59. export char version_id[100];
  60.  
  61. #ifdef NNTP
  62. export int use_nntp = 0;    /* bool: t iff we use nntp */
  63. #endif
  64.  
  65. export unsigned short user_eid;
  66. export unsigned short user_id, group_id;
  67. export int process_id;
  68. export int who_am_i;
  69. export int dont_write_console = 0;
  70. export int mail_errors_mode = 2;
  71.  
  72. extern int errno;
  73. struct passwd *getpwuid();
  74. extern char *getlogin(), *getenv();
  75.  
  76.  
  77. #ifdef HAVE_MULTIGROUP
  78. #ifndef NGROUPS
  79. #include <sys/param.h>
  80. #endif
  81. #ifndef GIDSET_TYPE
  82. #define GIDSET_TYPE int
  83. #endif
  84. static int ngroups;
  85. static GIDSET_TYPE gidset[NGROUPS];
  86.  
  87. static int
  88. in_grplist(gid)
  89. GIDSET_TYPE gid;
  90. {
  91.     int n;
  92.  
  93.     if (gid == group_id) return 1;
  94.  
  95.     for (n = 0; n < ngroups; ++n)
  96.     if (gidset[n] == gid) return 1;
  97.  
  98.     return 0;
  99. }
  100.  
  101. #define group_access(gpid)    in_grplist((GIDSET_TYPE)(gpid))
  102. #else
  103. #define group_access(gid)    ((gid) == group_id)
  104. #endif
  105.  
  106. /* signal handler interface */
  107.  
  108. export int s_hangup        = 0;    /* hangup signal */
  109. export int s_keyboard        = 0;    /* keyboard interrupt */
  110. export int s_pipe        = 0;    /* broken pipe */
  111. export int s_redraw        = 0;    /* redraw signal (if job control) */
  112. #ifdef RESIZING
  113. export int s_resized        = 0;    /* screen resized */
  114. #endif
  115.  
  116. #ifdef FAKE_INTERRUPT
  117. #include <setjmp.h>
  118.  
  119. export jmp_buf fake_keyb_sig;
  120. export int arm_fake_keyb_sig = 0;
  121. export char fake_keyb_siglist[NSIG];
  122. #endif
  123.  
  124. sig_type catch_hangup(n)
  125. int n;
  126. {
  127.     s_hangup = 1;
  128.     signal(n, SIG_IGN);
  129.  
  130. #ifdef FAKE_INTERRUPT
  131.     if (fake_keyb_siglist[n] && arm_fake_keyb_sig)
  132.         longjmp(fake_keyb_sig, 1);
  133. #endif
  134. }
  135.  
  136. static sig_type catch_keyboard(n)
  137. int n;
  138. {
  139. #ifdef RESET_SIGNAL_WHEN_CAUGHT
  140.     signal(n, catch_keyboard);
  141. #endif
  142. #ifdef FAKE_INTERRUPT
  143.     if (fake_keyb_siglist[n] && arm_fake_keyb_sig)
  144.     longjmp(fake_keyb_sig, 1);
  145. #endif
  146.     s_keyboard++;
  147. }
  148.  
  149. static sig_type catch_pipe(n)
  150. int n;
  151. {
  152.     s_pipe++;
  153.  
  154. #ifdef RESET_SIGNAL_WHEN_CAUGHT
  155.     signal(n, catch_pipe);
  156. #endif
  157. #ifdef FAKE_INTERRUPT
  158.     if (fake_keyb_siglist[n] && arm_fake_keyb_sig)
  159.         longjmp(fake_keyb_sig, 1);
  160. #endif
  161. }
  162.  
  163. #ifdef HAVE_JOBCONTROL
  164. static sig_type catch_suspend(n)
  165. int n;
  166. {
  167.     s_redraw++;
  168.  
  169. #ifdef RESET_SIGNAL_WHEN_CAUGHT
  170.     signal(n, catch_suspend);
  171. #endif
  172.  
  173.     suspend_nn();
  174.  
  175. #ifdef FAKE_INTERRUPT
  176.     if (fake_keyb_siglist[n] && arm_fake_keyb_sig) {
  177. #ifdef RESIZING
  178.     s_resized++;
  179. #endif
  180.     longjmp(fake_keyb_sig, 1);
  181.     }
  182. #endif
  183. }
  184. #endif
  185.  
  186.  
  187. int
  188. init_global()
  189. {
  190. #ifndef HAVE_UNISTD_H
  191.     unsigned short getuid();
  192.     int getpid();
  193. #endif
  194.  
  195. #ifdef FAKE_INTERRUPT
  196.     for (i = 0; i < NSIG; i++) fake_keyb_siglist[i] = i == 2 ? 1 : 0;
  197. #endif
  198.  
  199.     if (who_am_i != I_AM_NN) {
  200.     signal(SIGINT,  SIG_IGN);
  201.     signal(SIGQUIT, SIG_IGN);
  202.     }
  203.     signal(SIGTERM, catch_hangup);
  204.     signal(SIGHUP,  catch_hangup);
  205.     signal(SIGPIPE, catch_pipe);
  206.     signal(SIGALRM, SIG_IGN);
  207.  
  208. #ifdef SIGPWR
  209.     signal(SIGPWR, catch_hangup);
  210. #endif
  211.  
  212. #ifdef CONFIG_NUM_IN_VERSION
  213.     sprintf(version_id, "%s.%d%s #%d", RELEASE, PATCHLEVEL, BETA,
  214. #include "update.h"
  215.         );
  216. #else
  217.     sprintf(version_id, "%s.%d%s", RELEASE, PATCHLEVEL, BETA);
  218. #endif
  219.  
  220.     user_id = getuid();
  221.  
  222. #ifdef HAVE_MULTIGROUP
  223.     ngroups = getgroups(NGROUPS, gidset);    /* Get users's group set */
  224. #endif
  225.     group_id = getegid();
  226.     user_eid = geteuid();
  227.  
  228.     process_id = getpid();
  229.  
  230. #ifdef CLIENT_DIRECTORY
  231.     lib_directory = CLIENT_DIRECTORY;
  232. #else
  233.     lib_directory = LIB_DIRECTORY;
  234. #endif
  235.  
  236. #ifdef NEWS_DIRECTORY
  237.     news_directory = NEWS_DIRECTORY;
  238. #else
  239.     news_directory = "/usr/spool/news";
  240. #endif
  241.  
  242. #ifdef DB_DIRECTORY
  243.     db_directory = DB_DIRECTORY;
  244. #else
  245.     db_directory = mk_file_name(news_directory, ".nn");
  246. #endif
  247.  
  248. #ifdef ACCOUNTING
  249.     if (who_am_i == I_AM_ACCT)
  250.     return 0;
  251. #endif
  252.  
  253. #ifdef DB_DATA_DIRECTORY
  254.     db_data_directory = DB_DATA_DIRECTORY;
  255. #else
  256. #ifdef DB_DIRECTORY
  257.     db_data_directory = mk_file_name(db_directory, "DATA");
  258. #else
  259.     db_data_directory = NULL;
  260. #endif
  261. #endif
  262. #ifndef DB_LONG_NAMES
  263.     if (db_data_directory != NULL)
  264.     db_data_subdirs = file_exist(relative(db_data_directory, "0"), "dx");
  265. #endif
  266.  
  267. #ifdef NEWS_LIB_DIRECTORY
  268.     news_lib_directory = NEWS_LIB_DIRECTORY;
  269. #else
  270.     news_lib_directory = "/usr/lib/news";
  271. #endif
  272.  
  273.     /* this may later be changed by nntp_check */
  274.     news_active = mk_file_name(news_lib_directory, "active");
  275.  
  276. #ifdef MASTER_DIRECTORY
  277.     master_directory = MASTER_DIRECTORY;
  278. #else
  279.     master_directory = LIB_DIRECTORY;
  280. #endif
  281.  
  282. #ifdef HELP_DIRECTORY
  283.     help_directory = HELP_DIRECTORY;
  284. #else
  285.     help_directory = mk_file_name(lib_directory, "help");
  286. #endif
  287.  
  288. #ifdef LOG_FILE
  289.     log_file = LOG_FILE;
  290. #else
  291.     log_file = mk_file_name(LIB_DIRECTORY, "Log");
  292. #endif
  293.  
  294.     if (who_am_i == I_AM_SPEW)
  295.     return 0;
  296.  
  297.     signal(SIGINT,  catch_keyboard);
  298.     signal(SIGQUIT, catch_keyboard);
  299. #ifndef TK
  300. #ifdef HAVE_JOBCONTROL
  301.     signal(SIGTSTP, catch_suspend);
  302. #endif
  303. #endif /*TK*/
  304.  
  305.     if ((home_directory = getenv("HOME")) == NULL)
  306.     nn_exitmsg(1, "No HOME environment variable");
  307.  
  308.     if ((pager = getenv("PAGER")) == NULL)
  309.     pager = DEFAULT_PAGER;
  310.  
  311.     nn_directory = mk_file_name(home_directory, ".nn");
  312.  
  313.     if (!file_exist(nn_directory, "drwx")) {
  314.     if (who_am_i != I_AM_NN) return -1;
  315.     if (mkdir(nn_directory, 0755) < 0)
  316.         nn_exitmsg(1, "Cannot create %s directory", nn_directory);
  317.     return 1;
  318.     }
  319.  
  320.     return 0;
  321. }
  322.  
  323. void
  324. new_temp_file()
  325. {
  326.     static char buf[FILENAME];
  327.     static char *temp_dir = NULL;
  328.  
  329.     if (temp_dir == NULL)
  330.     if ((temp_dir = getenv("TMPDIR")) == NULL)
  331.         temp_dir = tmp_directory; /* just to make test above false */
  332.     else
  333.         tmp_directory = temp_dir;
  334.  
  335. #ifdef TK
  336.     sprintf(buf, "%s/nn_XXXXXX", tmp_directory);
  337. #else /* TK */
  338.     sprintf(buf, "%s/nn.XXXXXX", tmp_directory);
  339. #endif /* TK */
  340.     mktemp(buf);
  341.     temp_file = buf;
  342. }
  343.  
  344.  
  345. FILE *open_file(name, mode)
  346. char *name;
  347. int mode;
  348. {
  349.     FILE *f = NULL;
  350.     int fd;
  351.  
  352.     if ((mode & DONT_CREATE) && !file_exist(name, (char *)NULL))
  353.     return NULL;
  354.  
  355.     switch (mode & 0x0f) {
  356.  
  357.      case OPEN_READ:
  358.  
  359.     f = fopen(name, "r");
  360.     break;
  361.  
  362.      case OPEN_UPDATE:
  363.  
  364. /*    f = fopen(name, "r+");     -- not reliable on many systems (sigh) */
  365.  
  366.     if ((fd = open(name, O_WRONLY)) >= 0) {
  367.         if ((f = fdopen(fd, "w")) != NULL) return f;
  368.         close(fd);
  369.     }
  370.  
  371.     /* FALL THRU */
  372.      case OPEN_CREATE:
  373.  
  374.     f = fopen(name, "w");
  375.     break;
  376.  
  377.      case OPEN_APPEND:
  378.  
  379.     f = fopen(name, "a");
  380.     break;
  381.  
  382.      case OPEN_CREATE_RW:
  383.  
  384.     f = fopen(name, "w+");    /* not safe on all systems -- beware */
  385.     break;
  386.  
  387.      default:
  388.  
  389.     sys_error("Illegal mode: open_file(%s, 0x%x)", name, mode);
  390.     }
  391.  
  392.     if (f) {
  393.     if (mode & OPEN_UNLINK) unlink(name);
  394.     return f;
  395.     }
  396.  
  397.     if ((mode & MUST_EXIST) == 0) return NULL;
  398.  
  399.     sys_error("Cannot open file %s, mode=0x%x, errno=%d", name, mode, errno);
  400.  
  401.     return NULL;
  402. }
  403.  
  404. FILE *open_file_search_path(name, mode)
  405. char *name;
  406. int mode;
  407. {
  408.     FILE *f;
  409.  
  410.     if (name == NULL) return NULL;
  411.  
  412.     if (*name == '/') return open_file(name, mode);
  413.     
  414.     f = NULL;
  415.     if (!use_nntp)
  416.     f = open_file(relative(news_lib_directory, name), OPEN_READ);
  417.     if (f == NULL)
  418.     f= open_file(relative(lib_directory, name), OPEN_READ);
  419.     if (f == NULL)
  420.     f = open_file(relative(db_directory, name), OPEN_READ);
  421.  
  422.     return f;
  423. }
  424.  
  425. int    
  426. fgets_multi(buf, size, f)
  427. char *buf;
  428. int size;
  429. register FILE *f;
  430. {
  431.     register char *s = buf;
  432.     register int c, n = size;
  433.     
  434.     while (--n > 0) {
  435.     c = getc(f);
  436.     if (c == '\\') {
  437.         if ((c = getc(f)) == NL) continue;
  438.         *s++ = '\\';
  439.         if (--n < 0) break;
  440.     }
  441.     if (c == EOF) {
  442.         *s = NUL;
  443.         return s != buf;
  444.     }
  445.     if (c == NL) {
  446.         *s = NUL;
  447.         return 1;
  448.     }
  449.     *s++ = c;
  450.     }
  451.     buf[30] = NUL;
  452.     sys_error("Line too long \"%s...\" (max %d)", buf, size);
  453.     return 0;
  454. }
  455.     
  456. /*
  457.  *     relative -- concat directory name and file name
  458.  */
  459.  
  460. char *relative(dir, name)
  461. char *dir, *name;
  462. {
  463.     static char concat_path[FILENAME];
  464.  
  465.     sprintf(concat_path, "%s/%s", dir, name);
  466.     return concat_path;
  467. }
  468.  
  469.  
  470. char *mk_file_name(dir, name)
  471. char *dir, *name;
  472. {
  473.     char *buf;
  474.  
  475.     buf = newstr(strlen(dir) + strlen(name) + 2);
  476.     sprintf(buf, "%s/%s", dir, name);
  477.  
  478.     return buf;
  479. }
  480.  
  481.  
  482. char *home_relative(dir)
  483. char *dir;
  484. {
  485.     if (dir) {
  486.     if (*dir == '/')
  487.         return copy_str(dir);
  488.     else {
  489.         if (*dir == '~' && *++dir == '/') dir++;
  490.         return mk_file_name(home_directory, dir);
  491.     }
  492.     }
  493.     return NULL;
  494. }
  495.  
  496.  
  497. char *substchr(str, c1, c2)
  498. char *str, c1, c2;
  499. {
  500.     char *p;
  501.  
  502.     if ((p = strchr(str, c1)) != NULL) *p = c2;
  503.     return p;
  504. }
  505.  
  506. char *copy_str(str)
  507. char *str;
  508. {
  509.     char *new;
  510.  
  511.     new = newstr(strlen(str) + 1);
  512.     if (new) strcpy(new, str);
  513.  
  514.     return new;
  515. }
  516.  
  517. time_t m_time(f)
  518. FILE *f;
  519. {
  520.     struct stat st;
  521.  
  522.     if (fstat(fileno(f), &st) < 0) return 0;
  523.     return st.st_mtime;
  524. }
  525.  
  526.  
  527. time_t file_exist(name, mode)
  528. char *name;
  529. char *mode;
  530. {
  531.     static struct stat statb;
  532.     int mask;
  533.  
  534.     if (name != NULL && stat(name, &statb)) return 0;
  535.  
  536.     if (mode == NULL) return statb.st_mtime;
  537.  
  538.     if (statb.st_uid == user_eid)
  539.     mask = 0700;
  540.     else if (group_access(statb.st_gid))
  541.     mask = 0070;
  542.     else
  543.     mask = 0007;
  544.  
  545.     while (*mode) {
  546.     switch (*mode++) {
  547.     case 'd':
  548.         if ((statb.st_mode & S_IFMT) == S_IFDIR) continue;
  549.         errno = ENOTDIR;
  550.         return 0;
  551.     case 'f':
  552.         if ((statb.st_mode & S_IFMT) == S_IFREG) continue;
  553.         if ((statb.st_mode & S_IFMT) == 0000000) continue;
  554.         if ((statb.st_mode & S_IFMT) == S_IFDIR) {
  555.         errno = EISDIR;
  556.         return 0;
  557.         }
  558.         break;
  559.     case 'r':
  560.         if (statb.st_mode & mask & 0444) continue;
  561.         break;
  562.     case 'w':
  563.         if (statb.st_mode & mask & 0222) continue;
  564.         break;
  565.     case 'x':
  566.         if (statb.st_mode & mask & 0111) continue;
  567.         break;
  568.     }
  569.     errno = EACCES;
  570.     return 0;
  571.     }
  572.  
  573.     /* all modes are ok */
  574.     return statb.st_mtime;
  575. }
  576.  
  577. /*
  578.  * copy_file: copy (or append) src file to dest file.
  579.  *
  580.  * Returns number of characters copied or an error code:
  581.  *  -1: source file not found
  582.  *  -2: cannot create destination
  583.  *  -3: write error
  584.  */
  585.  
  586. int32 copy_file(src, dest, append)
  587. char *src, *dest;
  588. int append;
  589. {
  590.     register FILE *s, *d;
  591.     register int32 n = 0;
  592.     register int c;
  593.  
  594.     s = open_file(src, OPEN_READ);
  595.     if (s == NULL) return -1;
  596.  
  597.     d = open_file(dest, append ? OPEN_APPEND : OPEN_CREATE);
  598.     if (d == NULL) {
  599.     fclose(s);
  600.     return -2;
  601.     }
  602.  
  603.     n = 0;
  604.     while ((c = getc(s)) != EOF) {
  605.     putc(c, d);
  606.     n++;
  607.     }
  608.  
  609.     fclose(s);
  610.     if (fclose(d) == EOF) {
  611.     if (!append) unlink(dest);
  612.     return -3;
  613.     }
  614.     return n;
  615. }
  616.  
  617. /*
  618.  * move_file: move old file to new file, linking if possible.
  619.  *
  620.  * The third arg determines what is acceptable if the old file cannot be
  621.  * removed after copying to the new file:
  622.  *   0: must remove old, else remove new and fail,
  623.  *   1: must remove or truncate old, else remove new and fail,
  624.  *   2: just leave old if it cannot be removed or truncated.
  625.  *    
  626.  * Returns positive value for success, negative for failure:
  627.  *   0: file renamed (link)
  628.  *   1: file copied, old removed
  629.  *   2: file copied, but old file is only truncated.
  630.  *   3: file copied, but old file still exist.
  631.  *  -1: source file not found
  632.  *  -2: cannot create destination
  633.  *  -3: write error
  634.  *  -4: cannot unlink/truncate old
  635.  *  -5: cannot unlink new
  636.  *  -6: cannot link old to new
  637.  *  -9: messy situation: old and new linked on return (cannot happen?)
  638.  */
  639.  
  640. int
  641. move_file(old, new, may_keep_old)
  642. char *old, *new;
  643. int may_keep_old;
  644. {
  645.     int32 n;
  646.  
  647.     if (file_exist(new, (char *)NULL)) {
  648.     if (file_exist((char *)NULL, "d"))
  649.         return -5;
  650.     if (unlink(new) < 0)    /* careful - new may be directory ? */
  651.         switch (errno) {
  652.          case ENOENT:
  653.         break;
  654.          case EACCES:
  655.         if (file_exist((char *)NULL, "w")) goto do_copy;
  656.          default:
  657.         return -5;
  658.         }
  659.     }
  660.     
  661.     if (link(old, new) < 0)
  662.     switch (errno) {
  663.      case EACCES:    /* can just as well try to copy */
  664.      case EXDEV:
  665.         goto do_copy;
  666.      default:
  667.         return -6;
  668.     }
  669.     
  670.     if (unlink(old) == 0)
  671.     return 0;
  672.  
  673.     /* we were able to link but not unlink old    */
  674.     /* remove new, and attempt a copy instead    */
  675.     if (unlink(new) < 0) return -9; /* cannot happen? */
  676.  
  677.  do_copy:
  678.     if ((n = copy_file(old, new, 0)) < 0) return n;
  679.     if (unlink(old) == 0) return 1;
  680.     if (may_keep_old)
  681.     if (n == 0 || nn_truncate(old, (off_t)0) == 0) return 2;
  682.     if (may_keep_old == 2) return 3;
  683.     unlink(new);
  684.     return -4;
  685. }
  686. int
  687. save_old_file(name, suffix)
  688. char *name, *suffix;
  689. {
  690.     char buf[FILENAME];
  691.     sprintf(buf, "%s%s", name, suffix);
  692.     return move_file(name, buf, 0);
  693. }
  694.  
  695. #ifdef HAVE_SYSLOG
  696. #include <syslog.h>
  697. #endif /* HAVE_SYSLOG */
  698.  
  699.  
  700. static int
  701. enter_log(type, va_tail)
  702. char type;
  703. va_tdcl
  704. {
  705.     FILE *log;
  706.     char *msg1, buf[512];
  707.  
  708.     if (log_entry_filter != NULL)
  709.     for (msg1 = log_entry_filter; *msg1; msg1++)
  710.         if (*msg1 == type) return 1;
  711.  
  712.     msg1  = va_arg1(char *);
  713.     vsprintf(buf, msg1, va_args2toN);
  714.  
  715.     /* cannot use relative: one of the args may be generated by it */
  716.  
  717.     log = open_file(log_file, OPEN_APPEND);
  718.     if (log == NULL) return 0;
  719.  
  720.     fprintf(log, "%c: %s (%s): %s\n", type,
  721.         date_time((time_t)0), user_name(), buf);
  722.  
  723.     fclose(log);
  724.     return 1;
  725. }
  726.  
  727. /*VARARGS*/
  728. void sys_error(va_alist)
  729. va_dcl
  730. {
  731.     char buf[512];
  732.     char *fmt;
  733. #ifndef HAVE_SYSLOG
  734.     FILE *f;
  735. #endif
  736.     use_vararg;
  737.  
  738.     start_vararg;
  739.     enter_log('E', va_args1toN);
  740.     end_vararg;
  741.  
  742.     start_vararg;
  743.     fmt = va_arg1(char *);
  744.     vsprintf(buf, fmt, va_args2toN);
  745.     end_vararg;
  746.  
  747.     nn_exitmsg(1, "%s", buf);
  748. }
  749.  
  750. /*
  751.  *    sys_warning: like sys_error but MASTER will return with -1
  752.  *    instead of exit.  Clients still terminate!
  753.  */
  754.  
  755. /*VARARGS*/
  756. int
  757. sys_warning(va_alist)
  758. va_dcl
  759. {
  760.     char buf[512];
  761.     char *fmt;
  762. #ifndef HAVE_SYSLOG
  763.     FILE *f;
  764. #endif
  765.     static char *last_err = NULL;
  766.     use_vararg;
  767.  
  768.     start_vararg;
  769.     fmt = va_arg1(char *);
  770.     vsprintf(buf, fmt, va_args2toN);
  771.     end_vararg;
  772.  
  773.     if (last_err != NULL) {
  774.     if (strcmp(last_err, buf) == 0) return -1;
  775.     free(last_err);
  776.     }
  777.     last_err = copy_str(buf);
  778.  
  779.     start_vararg;
  780.     enter_log('R', va_args1toN);
  781.     end_vararg;
  782.  
  783.     nn_exitmsg(1, "%s", buf);
  784.     return -1;
  785. }
  786.  
  787. /*VARARGS*/
  788. int
  789. log_entry(va_alist)
  790. va_dcl
  791. {
  792.     int type, rval;
  793.     use_vararg;
  794.  
  795.     start_vararg;
  796.     type = va_arg1(int);
  797.     rval = enter_log(type, va_args2toN);
  798.     end_vararg;
  799.  
  800.     return rval;
  801. }
  802.  
  803. char *user_name()
  804. {
  805.     static char *user = NULL;
  806.     struct passwd *pw;
  807.  
  808.     if (user == NULL) {
  809.     user = getlogin();
  810.     if (user != NULL && *user != NUL) goto out;
  811.  
  812.     pw = getpwuid((int)user_id);
  813.     if (pw != NULL && pw->pw_name[0] != NUL) {
  814.         user = copy_str(pw->pw_name);
  815.         goto out;
  816.     }
  817.  
  818.     user = getenv("LOGNAME");
  819.     if (user != NULL && *user != NUL) goto out;
  820.     user = getenv("USER");
  821.     if (user != NULL && *user != NUL) goto out;
  822.     user = "?";
  823.     }
  824.  
  825.  out:
  826.     return user;
  827. }
  828.  
  829. time_t cur_time()
  830. {
  831.     time_t t;
  832.  
  833.     time(&t);
  834.     return t;
  835. }
  836.  
  837. char *date_time(t)
  838. time_t t;
  839. {
  840.     char *str;
  841.  
  842.     if (t == (time_t)0) t = cur_time();
  843.     str = ctime(&t);
  844.  
  845.     str[16] = 0;
  846.     return str+4;
  847. }
  848.  
  849. char *plural(n)
  850. long n;
  851. {
  852.     return n != 1 ? "s" : "";
  853. }
  854.  
  855. /*
  856.  *    memory management
  857.  */
  858.  
  859. /* #define MEM_DEBUG */        /* trace memory usage */
  860.  
  861. static void
  862. mem_error(t, bytes)
  863. int t;
  864. int32 bytes;
  865. {
  866.     char buf[200];
  867.  
  868.     if (t == 1) {
  869.     sprintf(buf, "Alloc failed: unsigned too short to represent %ld bytes",
  870.         (long)bytes);
  871.     } else {
  872.     sprintf(buf, "Out of memory - cannot allocate %ld bytes",
  873.         (long)bytes);
  874.     }
  875.  
  876.     sys_error(buf);
  877. }
  878.  
  879. char *mem_obj(size, nelt)
  880. unsigned size;
  881. int32 nelt;
  882. {
  883.     unsigned n;
  884.     char *obj;
  885.  
  886.     n = nelt;
  887.     if (n != nelt) mem_error(1, nelt);
  888.  
  889.     obj = calloc(n, size);
  890. #ifdef MEM_DEBUG
  891.     printf("CALLOC(%u,%u) => %lx\n", n, size, (long)obj);
  892. #endif
  893.     if (obj == NULL) mem_error(2, (int32)(size * nelt));
  894.     return obj;
  895. }
  896.  
  897. char *mem_str(nelt)
  898. int32 nelt;
  899. {
  900.     unsigned n;
  901.     char *obj;
  902.  
  903.     n = nelt;
  904.     if (n != nelt) mem_error(1, nelt);
  905.  
  906.     obj = malloc(n);
  907. #ifdef MEM_DEBUG
  908.     printf("MALLOC(%u) => %lx\n", n, (long)obj);
  909. #endif
  910.     if (obj == NULL) mem_error(2, nelt);
  911.     return obj;
  912. }
  913.  
  914. char *mem_resize(obj, size, nelt)
  915. char *obj;
  916. unsigned size;
  917. int32 nelt;
  918. {
  919.     unsigned n;
  920.     char *obj1;
  921.  
  922.     if (obj == NULL)
  923.     return mem_obj(size, nelt);
  924.  
  925.     nelt *= size;
  926.  
  927.     n = nelt;
  928.     if (n != nelt) mem_error(1, nelt);
  929.  
  930.     obj1 = realloc(obj, n);
  931. #ifdef MEM_DEBUG
  932.     printf("REALLOC(%lx, %u) => %lx\n", (long)obj, n, (long)obj1);
  933. #endif
  934.     if (obj1 == NULL) mem_error(2, (int32)size);
  935.     return obj1;
  936. }
  937.  
  938.  
  939. void
  940. mem_clear(obj, size, nelt)
  941. register char *obj;
  942. unsigned size;
  943. register int32 nelt;
  944. {
  945.     nelt *= size;
  946.     while (--nelt >= 0) *obj++ = NUL;
  947. }
  948.  
  949. void
  950. mem_free(obj)
  951. char *obj;
  952. {
  953. #ifdef MEM_DEBUG
  954.     printf("FREE(%lx)\n", (long)obj);
  955. #endif
  956.     if (obj != NULL) free(obj);
  957. }
  958.  
  959. #ifndef HAVE_MKDIR
  960.  
  961. mkdir(path, mode)
  962. char *path;
  963. int mode;
  964. {
  965.     char command[FILENAME*2 + 20];
  966.  
  967.     sprintf(command, "{ mkdir %s && chmod %o %s ; } > /dev/null 2>&1",
  968.         path, mode, path);
  969.     return system(command) != 0 ? -1 : 0;
  970. }
  971. #endif
  972.  
  973.  
  974. int
  975. nn_truncate(path, len)
  976. char *path;
  977. off_t len;
  978. {
  979. #ifdef HAVE_TRUNCATE
  980.  
  981.     /* Easy... we already have it... */
  982.     return truncate(path, len);
  983.  
  984. #else /* HAVE_TRUNCATE */
  985.  
  986.     int fd;
  987. #ifndef O_TRUNC
  988.     struct stat st;
  989. #endif
  990.  
  991.     if (len != 0)
  992.     sys_error("nn_truncate(%s,%ld): non-zero length", path, (long)len);
  993.  
  994. #ifdef O_TRUNC
  995.     fd = open(path, O_WRONLY | O_TRUNC);
  996. #else
  997.     if (stat(path, &st) < 0) return -1;
  998.     fd = creat(path, st.st_mode & 07777);
  999. #endif
  1000.     if (fd < 0) return -1;
  1001.     close(fd);
  1002.     return 0;
  1003. #endif
  1004. }
  1005.  
  1006. /* Should really have some sort of configure file and use strdup().. */
  1007. char *
  1008. strsave(s)
  1009. char *s;
  1010. {
  1011.     unsigned int l;
  1012.     char *buf = NULL;
  1013.     
  1014.     if (s) {
  1015.     l = strlen(s);
  1016.     buf = malloc(l + 1);
  1017.     if (buf) {
  1018.         if (l)
  1019.         strcpy(buf, s);
  1020.         else
  1021.         buf[0] = '\0';
  1022.     }
  1023.     }
  1024.     return buf;
  1025. }
  1026.  
  1027. char *
  1028. str3save(s1, s2, s3)
  1029. char *s1, *s2, *s3;
  1030. {
  1031.     unsigned int l;
  1032.     char *buf = NULL;
  1033.  
  1034.     if (!s1) s1 = "";
  1035.     if (!s2) s2 = "";
  1036.     if (!s3) s3 = "";
  1037.  
  1038.     l = strlen(s1) + strlen(s2) + strlen(s3);
  1039.  
  1040.     buf = malloc(l + 1);
  1041.     if (buf) {
  1042.     strcpy(buf, s1);
  1043.     strcat(buf, s2);
  1044.     strcat(buf, s3);
  1045.     }
  1046.     return buf;
  1047. }
  1048.  
  1049. /*
  1050.  * This quick function is a hack of a replacement for fgets, but is
  1051.  * unlimited in line length.  It returns a pointer to a buffer which
  1052.  * just happens to be malloc()ed and is reused next time.  IE: you had
  1053.  * best strdup() it. 
  1054.  *
  1055.  * This was inspired by an article I saw in alt.sources but forgot to
  1056.  * save.
  1057.  *
  1058.  * -Peter Wemm <peter@DIALix.oz.au>
  1059.  */
  1060.  
  1061. static char    *buf = NULL;    /* buffer for line */
  1062. static unsigned int    size = 0;    /* size of buffer */
  1063.  
  1064. #define INITIAL_CHUNK    256    /* Initial malloc size */
  1065. #define GROW_CHUNK    256    /* extend with mem_resize in units if this */
  1066.  
  1067. /* 
  1068.  * Get a LONG line.
  1069.  * Returns a pointer to a '\0' terminated string which will be
  1070.  * overwritten on the next call.
  1071.  * Returns NULL if error or eof, or if nothing could be read at all.
  1072.  *
  1073.  * Note: It does not react very well to '\0' characters in the byte stream
  1074.  */
  1075.  
  1076. char *
  1077. fgetstr(f)
  1078. FILE    *f;
  1079. {    
  1080.   unsigned int    len;        /* # of chars stored into buf before '\0' */
  1081.   char    *s;
  1082.  
  1083.   if (size == 0 && buf == NULL) {
  1084.     size = INITIAL_CHUNK;
  1085.     buf = mem_obj(size, 1);
  1086.     if (buf == NULL) {
  1087.       size = 0;
  1088.       nn_exitmsg(50, "cant allocate initial chunk\n");
  1089.       return NULL;
  1090.     }
  1091.   }
  1092.     
  1093.   len = 0;
  1094.  
  1095.   while (fgets(buf + len, size - len, f) != NULL)
  1096.   {
  1097.     len += strlen(len + buf);
  1098.     if (len > 0 && buf[len - 1] == '\n')
  1099.       break;            /* the whole line has been read */
  1100.  
  1101.     size += GROW_CHUNK;
  1102.     s = mem_resize(buf, size, 1);
  1103.     
  1104.     if (!s) {            /* Malloc failure */
  1105.       free(buf);        /* panic... */
  1106.       size = 0;
  1107.       buf = NULL;
  1108.       nn_exitmsg(51, "out of memory: cant mem_resize chunk\n");
  1109.       return NULL;
  1110.     }
  1111.     buf = s;
  1112.     mem_clear(buf + len, size - len, 1);
  1113.   }
  1114.  
  1115.   if (len == 0) {
  1116.     return NULL;        /* nothing read (eof or error) */
  1117.   }
  1118.  
  1119.   /* For when we are reading from a NNTP stream. */
  1120.   if (len > 1 && buf[len - 1] == '\n')
  1121.     --len;
  1122.   if (len > 1 && buf[len - 1] == '\r')
  1123.     --len;
  1124.   buf[len] = '\0';        /* unconditionally terminate string, */
  1125.                 /* possibly overwriting CR and/or NL */
  1126.   return buf;
  1127. }
  1128.